const net = require('net');
let bsplit = require('buffer-split');
const os = require('os');
const request = require('co-request');
const co = require('co');
const WebSocketServer = os.platform() === 'win32' ? require('ws').Server : require('uws').Server;

const API = require('./API');
const config = require('./environment');
const deviceLib = require('../lib/device');


exports.listenStreamWS = () => {
  const wss = new WebSocketServer({ port: config.streamWebSocketServer.port });

  wss.on('connection', (ws) => {
    ws.on('message', (message) => {
      const msg = JSON.parse(message);
      if (msg.type === 'opt' && ws.clientOfStreamServer) {
        const opts = msg.data.split(',');
        for (let elem of opts.valueOf()) {
          const packageLen = new Buffer(4);
          if (elem[0] !== 'k') elem += '\n';
          packageLen.writeUInt32LE(elem.length, 0);
          ws.clientOfStreamServer.write(packageLen + elem);
        }
      } else if (msg.type === 'h264') {
        if (ws.clientOfStreamServer) {
          ws.clientOfStreamServer.end();
        }

        co(function*() {
          const result = yield request(`${API.centralServer}/uuid=${msg.uuid}`);
          const streamServerConfig = JSON.parse(result.body);
          if (streamServerConfig.token && streamServerConfig.use_v2) {
            ws.clientOfStreamServer = new net.Socket();
            ws.clientOfStreamServer.connect(
              streamServerConfig.port, streamServerConfig.ip, () => {
                const packageLen = new Buffer(4);
                packageLen.writeUInt32LE(streamServerConfig.token.length, 0);
                ws.clientOfStreamServer.write(packageLen + streamServerConfig.token);
              });
            ws.clientOfStreamServer.on('data', (data) => {
              ws.send(data.toString('base64'), (err) => {
                if (err) {
                  if (ws.clientOfStreamServer) ws.clientOfStreamServer.end();
                }
              });
            });
            ws.clientOfStreamServer.on('end', () => {
              console.log('disconnected from stream server');
            });
            ws.deviceID = msg.uuid;
          } else {
            throw streamServerConfig.reason;
          }
        }).catch((err) => {
          console.error(err.stack);
          if (ws.clientOfStreamServer) {
            ws.clientOfStreamServer.end();
          }
        });
      } else if (msg.type === 'img') {
        if (ws.clientOfStreamServer) {
          ws.clientOfStreamServer.end();
        }

        co(function*() {
          const result = yield request(`${API.centralServer}/uuid=${msg.uuid}`);
          const streamServerConfig = JSON.parse(result.body);
          if (streamServerConfig.token && !streamServerConfig.use_v2) {
            let bufferImg = null;
            let img = null;
            ws.clientOfStreamServer = new net.Socket();
            ws.clientOfStreamServer.connect(streamServerConfig.port, streamServerConfig.ip, () => {
              console.log(`connected to stream server, ip: ${streamServerConfig.ip}, port: ${streamServerConfig.port}`);
              const packageLen = new Buffer(4);
              packageLen.writeUInt32LE(streamServerConfig.token.length, 0);
              ws.clientOfStreamServer.write(packageLen + streamServerConfig.token);
            });

            ws.clientOfStreamServer.on('data', (data) => {
              bufferImg = bufferImg ? Buffer.concat([bufferImg, data], bufferImg.length + data.length) : data;
              if (bufferImg.length > 4 && bufferImg.length >= bufferImg.readUInt32LE(0) + 4) {
                img = bufferImg.slice(4, 4 + bufferImg.readUInt32LE(0)).toString('base64');
                bufferImg = bufferImg.slice(bufferImg.readUInt32LE(0) + 4, bufferImg.length);
                ws.send(img, (err) => {
                  if (err) {
                    console.error(`send img data error: ${err}`);
                    if (ws.clientOfStreamServer) {
                      ws.clientOfStreamServer.end();
                    }
                  }
                });
              }
            });

            ws.clientOfStreamServer.on('end', () => {
              console.log('disconnected clientOfStreamServer');
            });
          } else {
            throw streamServerConfig.reason;
          }
        }).catch((err) => {
          console.error(err.stack);
          if (ws.clientOfStreamServer) {
            ws.clientOfStreamServer.end();
          }
        });
      }
    });

    ws.on('close', () => {
      console.log('WebSocket close');
      if (ws.clientOfStreamServer) {
        ws.clientOfStreamServer.end();
      }
      if (ws.deviceID) {
        deviceLib.release(ws.deviceID)
          .then(function (ret) {
            console.log(`release device ${ws.deviceID}, result ${ret}`);
          }, function (err) {
            console.error(err.stack);
          })
      }
    });

    ws.on('error', (err) => {
      console.log(`A WebSocket error occurred: ${err}`);
    });
  });

  exports.streamWebSocketServer = wss;
};


exports.listenLogWS = () => {
  const wss = new WebSocketServer({ port: config.logWebSocketServer.port });

  wss.on('connection', (ws) => {
    let logData = undefined;
    let flag = true;
    let level = '2';
    let levelHash = [
      /I|W|E/,
      /E|W/,
      /E/,
    ];
    ws.on('message', (message) => {
      message = JSON.parse(message);
      if(message.uuid) {
        const uuid = message.uuid;
        if (ws._clientOfLogServer) {
          ws._clientOfLogServer.end();
        }
        co(function*() {
          const result = yield request(`${API.centralServer}/uuid=${uuid}`);
          const cfg = JSON.parse(result.body);
          if (cfg.token) {
            ws._clientOfLogServer = net.connect(
              cfg.logport, cfg.ip, () => {
                console.log('connected to stream server');
                const packageLen = new Buffer(4);
                packageLen.writeUInt32LE(cfg.token.length, 0);
                ws._clientOfLogServer.write(packageLen + cfg.token);
              });
            ws._clientOfLogServer.on('data', (data) => {
              //fliter data
              if(logData) {
                logData = Buffer.concat([logData, data], logData.length + data.length);
              }
              else {
                logData = data;
              }
              let result = bsplit(logData, new Buffer('\r\n'));
              let list = [];
              let logcatLine = undefined;
              let temp;
              let res;
              result.reverse();
              while (result.length) {
                temp = result.shift().toString();
                res = temp.match(/^\[ *(\d{2}\-\d{2}) *(\d{2}:\d{2}:\d{2}.\d{3}) *(\d+) *: *(\d+) ([V|D|I|W|E])\/([a-zA-Z0-9_\.]+) *\]$/);
                if (res &&
                  res[5].match(levelHash[level])) {
                  if (logcatLine) {
                    list.unshift(logcatLine);
                  }
                  logcatLine = {
                    date: res[1],
                    time: res[2],
                    pid: res[3],
                    tid: res[4],
                    level: res[5],
                    tag: res[6],
                    text: ""
                  }
                }
                else if (logcatLine){
                  logcatLine['text'] += temp;
                }else {
                  logcatLine = undefined;
                }
              }
              if (logcatLine) {
                list.unshift(logcatLine);
              }

              //if(flag) {
              flag = false;
              ws.send(JSON.stringify({data: list}), (err) => {
                if (err) {
                  console.error(`send log data error: ${err}`);
                  if (ws._clientOfLogServer) ws._clientOfLogServer.end();
                }
                //setTimeout(function () {
                flag = true;
                //}, 200)
              });
              logData = '';
              //}
            });
            ws._clientOfLogServer.on('end', () => {
              console.log('disconnected _clientOfLogServer');
            });
          } else {
            throw cfg.reason;
          }
        }).catch((err) => {
          console.error(err.stack);
          if (ws._clientOfLogServer) {
            ws._clientOfLogServer.end();
          }
        });
      }
      else if(message.level) {
        console.log('change to level:', message.level);
        level = message.level;
      }
      else {
        if (ws._clientOfLogServer) {
          ws._clientOfLogServer.end();
        }
      }
    });

    ws.on('close', () => {
      console.log('Logcat WebSocket close: do something here!');
    });

    ws.on('error', (err) => {
      console.log(`A logcat WebSocket error occurred: ${err}`);
    });
  });

  exports.logWebSocketServer = wss;
};
